﻿package cocoaas3.flash{
	import jp.progression.casts.*;
	import jp.progression.commands.display.*;
	import jp.progression.commands.lists.*;
	import jp.progression.commands.managers.*;
	import jp.progression.commands.media.*;
	import jp.progression.commands.net.*;
	import jp.progression.commands.tweens.*;
	import jp.progression.commands.*;
	import jp.progression.data.*;
	import jp.progression.events.*;
	import jp.progression.scenes.*;
	import jp.nium.core.debug.Logger;
	import flash.events.*;
	import flash.display.*;
	import flash.geom.*;
	import flash.utils.*;

	import cocoaas3.*;
	import cocoaas3.protocol.*;

	public class UITableView extends UIScrollView {

		//
		// original member
		//
		public var delegate:UITableViewDelegate;
		public var dataSource:UITableViewDataSource;
		public var allowsSelection:Boolean = true;
		private var _backgroundView:UIView = new UIView();
		public function get backgroundView():UIView{
			return _backgroundView;
		}

		//
		// custom member
		//
		private var _stageWidth:uint;// CastDocument.bottom などに直接アクセスると激遅いので、キャッシュをもたせる
		private var _stageHeight:uint;// 同上

		private var _cellCache_array:Array = new Array();
		private var _cellRectData_vector:Vector.<Rectangle>;
		private var _originCellIndex_num:uint = 0;// セルの走査をするときの基準インデックス
		private var _reusableCellQueue_array:Array = new Array();
		private var _cellIndexPathInfo_dic:Dictionary = new Dictionary();// 各セルの NSIndexPath をストア
		private var _refleshList:SerialList = new SerialList();// 軽量化のために使いまわす

		private var _lastIndexPathsForVisibleRows_vector:Vector.<NSIndexPath> = new Vector.<NSIndexPath>();// 直前のフレームの、indexPathsForVisibleRowsを ストア

		private var _posYOnMouseDown:int;// タップを開始したときのスクロール位置( セルタップの判定に使う )


		public function UITableView( size:CGSize ){
			super( size );
			this.bounds = new CGRect( 0, 0, size.width, size.height );
			addChild( backgroundView );
			addChild( subviewContainer );
		}


		//-----------------------------------------------------------------------------------------
		//
		// original functions
		//
		//-----------------------------------------------------------------------------------------


		// 可視領域にある行のインデックスパスを配列で返す。
		public function indexPathsForVisibleRows():Vector.<NSIndexPath>{
			var rect = new CGRect( 0, subviewContainer.scrollRect.y, this.bounds.width, this.bounds.height );
			var result_vector:Vector.<NSIndexPath> = indexPathsForRowsInRect( rect );
			return result_vector;
		}

		// 可視領域にある UITableViewCell の vector を返す
		public function visibleCells():Vector.<UITableViewCell>{
			var visibleRows_vec:Vector.<NSIndexPath> = indexPathsForVisibleRows();
			var out_vec:Vector.<UITableViewCell> = new Vector.<UITableViewCell>();
			var len:uint = visibleRows_vec.length;
			for( var i:uint=0; i<len; i++ ){
				var ip = visibleRows_vec[i];
				var cell = cellForRowAtIndexPath( ip );
				out_vec.push( cell );
			}			
			return out_vec;
		}






		//-----------------------------------------------------------------------------------------
		//
		// custom functions
		//
		//-----------------------------------------------------------------------------------------
		override internal function onAddedToStage(e:Event):void{
			trace( "UITableView onAddedToStage" );
			super.onAddedToStage(e);
			this.addEventListener( "onMoveUp", _onMoveUp );
			this.addEventListener( "onMoveDown", _onMoveDown );
			//refreshProfiler.start();
		}

		override internal function onRemovedFromStage(e:Event):void{
			trace( "UITableView onRemovedFromStage" );
			super.onRemovedFromStage(e);
			this.removeEventListener( "onMoveUp", _onMoveUp );
			this.removeEventListener( "onMoveDown", _onMoveDown );
		}

		private function _onMoveUp( e:Event ):void{
			refresh();
		}
		private function _onMoveDown( e:Event ):void{
			refresh();
		}


		// データからセルの位置、高さデータを作る
		public function reloadData():void{
			var len:uint = numberOfRowsInSection( 0 );
			_cellRectData_vector = new Vector.<Rectangle>();
			var indexPath:NSIndexPath;
			var currentPosY:uint = 0;
			var height_num:uint = 0;
			var cellRect:Rectangle;
			for( var i:uint=0; i<len; i++ ){
				indexPath = new NSIndexPath( i, 0 );
				height_num = heightForRowAtIndexPath( indexPath );

				// cellRect作成
				cellRect = new Rectangle( 0, currentPosY, 640, height_num );
				_cellRectData_vector.push( cellRect );

				currentPosY += height_num;
			}
			contentSize = new CGSize( 640, currentPosY );
			_stageWidth = CastDocument.right;
			_stageHeight = CastDocument.bottom - 88;

			refresh();
		}


		// indexPath の diff をする
		// array["add"] = Vector.<NSIndexPath>
		// array["remove"] = Vector.<String>
		// という構造の配列を返します。
		private function _diffIndexPaths( old_vector:Vector.<NSIndexPath>, new_vector:Vector.<NSIndexPath> ):Array{
			var oldNotInNew:Vector.<String> = new Vector.<String>();
			var newNotInOld:Vector.<NSIndexPath> = new Vector.<NSIndexPath>();
			var oldLookup:Array = new Array();
			var i:int;
			var len:uint;
			var str:String;

			len = old_vector.length;
			for( i=0; i<len; i++ ){
				str = old_vector[i].toString(true);
			    oldLookup[str] = true;
			}

			len = new_vector.length;
			for( i=0; i<len; i++ ){
				str = new_vector[i].toString(true);
			    if( oldLookup[str] ){
			        delete oldLookup[str];
			    } else {
			        newNotInOld.push( new_vector[i] );
			    }
			}

			len = oldLookup.length;
			for( var k:String in oldLookup ){
			    oldNotInNew.push( k );
			}

			var result_array:Array = new Array();
			result_array["add"] = newNotInOld;
			result_array["remove"] = oldNotInNew;
			return result_array;
		}


		// 表示更新
		public function refresh():void{
			var indexPath_vector:Vector.<NSIndexPath> = indexPathsForVisibleRows();// 表示するべきセルのindexPath

			var diffedIndexPathsForVisibleRows:Array = _diffIndexPaths( _lastIndexPathsForVisibleRows_vector, indexPath_vector );
			_lastIndexPathsForVisibleRows_vector = indexPath_vector;


			var i:uint, len:uint, ip:NSIndexPath, cell:UITableViewCell;

			var addedIndexPaths_vector:Vector.<NSIndexPath> = diffedIndexPathsForVisibleRows["add"];
			len = addedIndexPaths_vector.length;
			for( i=0; i<len; i++ ){
				ip = addedIndexPaths_vector[i];
				cell = cellForRowAtIndexPath( ip );
				cell.y = rectForRowAtIndexPath( ip ).y;
				cell.addEventListener( MouseEvent.CLICK, _onCellClick );
				this.addSubview( cell );
			}

			var removedIndexPaths_vector:Vector.<String> = diffedIndexPathsForVisibleRows["remove"];
			if( removedIndexPaths_vector.length ){
				var cellsToRemove_vector:Vector.<UITableViewCell> = new Vector.<UITableViewCell>();
				var currentChildren:Array = new Array();
				for( i=0; i<subviewContainer.numChildren; i++ ){
					currentChildren.push( subviewContainer.getChildAt(i) );
				}
				// 削除するセルを求める
				len = currentChildren.length;
				for( i=0; i<len; i++ ){
					cell = currentChildren[i] as UITableViewCell;
					if( cell ){
						ip = indexPathForCell( cell );
						if( removedIndexPaths_vector.indexOf( ip.toString(true) ) > -1 ){
							cellsToRemove_vector.push( cell );
						}
					}
				}
				// 実際に削除
				len = cellsToRemove_vector.length;
				for( i=0; i<len; i++ ){
					cell = cellsToRemove_vector[i];
					cell.removeEventListener( MouseEvent.CLICK, _onCellClick );
					subviewContainer.removeChild( cell );
					_cacheReusableCell( cell );
				}
			}
			
		}



		private function _onCellClick( e:MouseEvent ):void{
			if( allowsSelection ){
				var cell:UITableViewCell = e.target as UITableViewCell;
				var indexPath:NSIndexPath = indexPathForCell( cell );
				didSelectRowAtIndexPath( indexPath );
			}
		}





		// 指定した矩形で囲まれている行のインデックスパスを配列で返す。
		private function indexPathsForRowsInRect( rect:CGRect ):Vector.<NSIndexPath>{
			//trace( _cellRectData_vector );
			if( !_cellRectData_vector || _cellRectData_vector.length==0 ){
				return new Vector.<NSIndexPath>();
			}

			var downward_vector:Vector.<NSIndexPath> = new Vector.<NSIndexPath>();
			var upward_vector:Vector.<NSIndexPath> = new Vector.<NSIndexPath>();
			var ip:NSIndexPath;

			var len:uint = _cellRectData_vector.length;
			var startCount:uint = _originCellIndex_num;
			// 基準点から下に向かって走査
			for( var i:int=startCount; i<len; i++ ){
				if( rect.intersects(_cellRectData_vector[i]) ){
					//ip = i+ ",0";
					ip = new NSIndexPath( i, 0 );
					downward_vector.push( ip );
				} else {
					if( downward_vector.length > 0 ){
						// これ以上 下には無いので終了
						break;
					}
				}
			}
			
			// 基準点から上に向かって走査
			if( startCount > 0 ){
				for( i=startCount-1; i>=0; i-- ){
					if( rect.intersects(_cellRectData_vector[i]) ){
						//ip = i+ ",0";
						ip = new NSIndexPath( i, 0 );
						upward_vector.push( ip );
					} else {
						if( i == startCount-1 && upward_vector.length==0 ){
							break;
						}
						if( upward_vector.length > 0 ){
							// これ以上 上には無いので終了
							break;
						}
					}
				}
			}



			// 一番上に表示される基準セルインデックスを更新
			var top_ip:NSIndexPath;
			if( upward_vector.length == 0 ){
				top_ip = downward_vector[0];
			} else {
				top_ip = upward_vector[upward_vector.length-1];
			}
			_originCellIndex_num = _getSerialNumber( top_ip );

			var result_vector:Vector.<NSIndexPath> = downward_vector.concat( upward_vector );

			return result_vector;
		}

		// 指定したテーブルビューセルのインデックスパスを返す。
		private function indexPathForCell( cell:UITableViewCell ):NSIndexPath{
			return _cellIndexPathInfo_dic[cell];
		}

		// 指定したインデックスパスの行の描画領域を返す。
		private function rectForRowAtIndexPath( indexPath:NSIndexPath ):Rectangle{
			// すべてのセクションを通した通し番号を取得
			var index:uint = _getSerialNumber( indexPath );
			return _cellRectData_vector[index];
		}





		// 指定した NSIndexPath の、通し番号を返す
		private function _getSerialNumber( indexPath:NSIndexPath ):uint{
			var index:uint = 0;
			var sectionCount:uint = indexPath.section;
			for( var i:uint=0; i<sectionCount; i++ ){
				index += numberOfRowsInSection( i );
			}
			index += indexPath.row;
			return index;
		}





		// セルをキャッシュ( セルが画面外にでて remove されたときによぶ )
		private function _cacheReusableCell( cell:UITableViewCell ):void{
			if( !_reusableCellQueue_array[cell.reuseIdentifier] ){
				_reusableCellQueue_array[cell.reuseIdentifier] = new Vector.<UITableViewCell>();
			}
			_reusableCellQueue_array[cell.reuseIdentifier].push( cell );
		}


		// 遊んでいるセルがあったら、処理軽減のためにそれを返す
		public function dequeueReusableCellWithIdentifier( identifier:String ):UITableViewCell{
			if( !_reusableCellQueue_array[identifier] ){
				return null;
			}

			if( _reusableCellQueue_array[identifier].length == 0 ){
				return null;
			} else {
				return _reusableCellQueue_array[identifier].pop();
			}

		}

		public function scrollToRowAtIndexPath( indexPath:NSIndexPath, atScrollPosition:String="none", animated:Boolean=true ){
			var rect:Rectangle = rectForRowAtIndexPath( indexPath );
			var ty:int = 0 - rect.y;
			new DoTweener( this, {y:ty, time:0.5, transition:"easeInOutQuart"} ).execute();
		}


		private function numberOfSections():uint{
			return 1;
		}

		//
		// デリゲートメソッド
		//
		public function cellForRowAtIndexPath( indexPath:NSIndexPath ):UITableViewCell{
			var cell:UITableViewCell = dataSource.cellForRowAtIndexPath( indexPath );
			_cellIndexPathInfo_dic[cell] = indexPath;
			return cell;
		}
		public function numberOfRowsInSection( sectionId:uint ):uint{
			return dataSource.numberOfRowsInSection( sectionId );
		}
		public function heightForRowAtIndexPath( indexPath:NSIndexPath ):uint{
			return delegate.heightForRowAtIndexPath( indexPath );
		}
		public function didSelectRowAtIndexPath( indexPath:NSIndexPath ):void{
			delegate.didSelectRowAtIndexPath( indexPath );
		}










	}
}


